package com.carcenter.order.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.carcenter.common.constant.appointment.AppointmentConstant;
import com.carcenter.common.exception.Assert;
import com.carcenter.common.pojo.order.AppointmentMessage;
import com.carcenter.common.pojo.personnel.UserDto;
import com.carcenter.common.result.Result;
import com.carcenter.common.pojo.order.MonthSale;
import com.carcenter.common.result.enums.impl.BusinessCode;
import com.carcenter.common.result.enums.impl.ResultCode;
import com.carcenter.order.entity.*;
import com.carcenter.order.service.*;
import com.carcenter.order.service.AppointmentTrasationalService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.personnel.client.StaffApi;
import com.store.client.StoreApi;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.MessageDeliveryMode;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author car
 * @since 2025-07-16
 */


@Slf4j
@RestController
@RequestMapping("/appointment-master")
public class AppointmentMasterController {

    @Autowired
    IAppointmentMasterService appointmentMasterService;
    @Autowired
    IAppointmentProcessService appointmentProcessService;
    @Autowired
    IAppointmentDetailService appointmentDetailService;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    StoreApi storeApi;

    @Autowired
    StaffApi staffApi;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    private AppointmentTrasationalService appointmentService;


    // 限流注解（需要引入Sentinel或Hystrix）
    // 使用Hystrix进行限流和熔断（替代Sentinel）
    @HystrixCommand(
            fallbackMethod = "handleFlowQpsException", // 降级方法
            commandProperties = {
                    // 配置QPS限流阈值（每秒最多处理20个请求）
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "10000"), // 超时时间
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "100"), // 熔断触发的请求数阈值
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50") // 错误率阈值（50%）
            },
            threadPoolProperties = {
                    // 配置线程池（处理该接口的专用线程池）
                    @HystrixProperty(name = "coreSize", value = "10"), // 核心线程数
                    @HystrixProperty(name = "maxQueueSize", value = "50"), // 队列大小
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "30") // 队列拒绝阈值
            }
    )
    @PostMapping("/newAppointment")
    public Result<String> newAppointment(@Valid @RequestBody NewAppointmentDto dto) {
        // 1. 验证用户当天预约次数
        long count = appointmentService.countUserAppointmentsToday(dto.getUserId());
        Assert.error(count >= 3, ResultCode.PERMISSION_EXCESS_APPOINTMENT);

        // 2. 分布式锁
        String startTimeStr = dto.getStartTime().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String endTimeStr = dto.getEndTime().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String lockKey = "STATION_COUNT_LOCK:" + dto.getStoreId() + ":" + startTimeStr + "--" + endTimeStr;
        RLock lock = redissonClient.getLock(lockKey);

        try {
            boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
            if (!isLocked) {
                return Result.fail(ResultCode.SYSTEM_INNER_BUSY.getMessage());
            }

            // 3. 事务外获取库存（关键优化点）
            Integer stationCount = appointmentService.getStationCount(dto.getStoreId());
            if (stationCount == null || stationCount <= 0) {
                return Result.fail(ResultCode.PERMISSION_EXCESS_STATIONACCOUNT.getMessage());
            }

            // 4. 查询同时间段已支付的预约
            List<Integer> appointmentIds = appointmentMasterService.listObjs(
                    new LambdaQueryWrapper<AppointmentMaster>()
                            .ge(AppointmentMaster::getStartTime, dto.getStartTime())
                            .le(AppointmentMaster::getEndTime, dto.getEndTime())
                            .select(AppointmentMaster::getId),
                    obj -> Integer.valueOf(obj.toString())
            );

            log.info("appointmentIds容量大小={}", appointmentIds);
            // 5. 计算实际可用库存
            if (!appointmentIds.isEmpty()) {
                List<AppointmentProcess> processes = appointmentProcessService.list(
                        new LambdaQueryWrapper<AppointmentProcess>()
                                .in(AppointmentProcess::getAppointmentId, appointmentIds)
                                .orderByAsc(AppointmentProcess::getAppointmentId)
                                .orderByAsc(AppointmentProcess::getOperationTime)
                );

                Map<Integer, List<AppointmentProcess>> processMap = processes.stream()
                        .collect(Collectors.groupingBy(AppointmentProcess::getAppointmentId));

                for (Integer appointmentId : appointmentIds) {
                    List<AppointmentProcess> aps = processMap.getOrDefault(appointmentId, Collections.emptyList());
                    if (aps.size()==1) {
                        stationCount--;
                    }
                    if (aps.size() >= 2) {
                        AppointmentProcess second = aps.get(1);
                        AppointmentProcess third = aps.size() >= 3 ? aps.get(2) : null;
                        if (AppointmentConstant.ALREADY_PAID.equals(second.getAppointmentStatus()) &&
                                (third == null || AppointmentConstant.IN_SERVICE.equals(third.getAppointmentStatus()))) {
                            stationCount--;
                        }
                    }
                }
            }

            // 6. 库存不足校验
            //        if (stationCount <= 0) {
            //            throw new BusinessException(ResultCode.PERMISSION_EXCESS_STATIONACCOUNT);
            //        }
            Assert.error(stationCount <= 0,ResultCode.PERMISSION_EXCESS_STATIONACCOUNT);


            // 7. 执行事务逻辑
            Result<Integer> result = appointmentService.createAppointment(dto, stationCount);

            //8,mq通知
            if (result!=null && result.getCode()==ResultCode.SUCCESS.getCode()) {
                AppointmentMessage message = new AppointmentMessage();
                message.setAppointmentId(result.getData());
                message.setUserId(dto.getUserId());
                // 生产者发送时设置10分钟过期
                rabbitTemplate.convertAndSend("appointment.exchange", "appointment.key.normal", message, m -> {
                    m.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                    m.getMessageProperties().setExpiration("600000"); // 过期时间（毫秒），10分钟
                    return m;
                });
                return Result.ok(BusinessCode.ADD_SUCCESS.getMessage());
            }else {
                return Result.fail(BusinessCode.ADD_FAIL.getMessage());
            }

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("获取锁被中断", e);
            return Result.fail(ResultCode.SYSTEM_INNER_ERROR.getMessage());
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    // Hystrix降级方法（限流/熔断时触发）
    // 注意：参数列表必须与原方法一致
    // 降级方法添加Throwable参数（Hystrix会自动传入异常）
    public Result<String> handleFlowQpsException(@Valid @RequestBody NewAppointmentDto dto, Throwable e) {
        log.warn("触发限流或熔断，userId={}，原因：{}", dto.getUserId(), e.getMessage(), e);
        return Result.fail(e.getMessage());
    }



    @PostMapping("/getMonthSalesByStoreIds/{month}")
    public Result<List<MonthSale>> getMonthSalesByStoreIds(@RequestBody List<Integer> storeIds, @PathVariable String month) {

        //String month = "2025年07月"; // 从前端接收的字符串
        Month2Day month2Day = getMonth2Day(month);

        List<MonthSale> monthSales=new ArrayList<>();
        if (storeIds!=null && storeIds.size()>0) {
            for (Integer storeId : storeIds) {
                MonthSale monthSale = appointmentMasterService.getMonthAppointmentDetail(storeId, month2Day.getFirstDay(), month2Day.getLastDay());
                monthSales.add(monthSale);
            }
        }

        return Result.ok(monthSales);


        //入参，门店ID，月份；
        //根据门店ID    查在预约主表查询，预约ID和时间；
        //在预约流程表，根据预约ID查询已完成预约订单；
        //剩余的预约ID，在预约明细表查询对应金额；
        //返回的是，某个月，所有有效的预约明细表；

    }

    public Month2Day getMonth2Day(String month) {
        // 定义解析格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月", Locale.CHINA);
        // 解析为YearMonth对象
        YearMonth yearMonth = YearMonth.parse(month, formatter);
        // 计算当月第一天（00:00:00）
        LocalDateTime firstDay = yearMonth.atDay(1).atStartOfDay();
        // 计算当月最后一天（23:59:59）
        LocalDateTime lastDay = yearMonth.atEndOfMonth().atTime(23, 59, 59);
        return new Month2Day(firstDay, lastDay);
    }

    @PostMapping("/getMonthAppointmentByMonthSearch")
    public Result<List<DailyData>> getMonthAppointmentByMonthSearch(@RequestBody MonthSearchDto monthSearchDto) {

        LambdaQueryWrapper<AppointmentMaster> queryWrapper = new LambdaQueryWrapper<>();
        if (monthSearchDto.getStoreId()!=null && monthSearchDto.getStoreId()>0) {
            queryWrapper.eq(AppointmentMaster::getStoreId,monthSearchDto.getStoreId());
        }

        if (monthSearchDto.getSearchMonth()!=null && !monthSearchDto.getSearchMonth().equals("")) {
            Month2Day month2Day = getMonth2Day(monthSearchDto.getSearchMonth());
            queryWrapper.ge(AppointmentMaster::getStartTime,month2Day.getFirstDay());
            queryWrapper.le(AppointmentMaster::getEndTime,month2Day.getLastDay());
        }
        List<AppointmentMaster> appointmentMasterList = appointmentMasterService.list(queryWrapper);

        List<Integer> appointmentIds = appointmentMasterList.stream().map(e -> e.getId()).collect(Collectors.toList());
        Map<Integer, Boolean> booleanMap = appointmentFinishedFlag(appointmentIds);
        log.info("appointmentMasterList:{}",appointmentMasterList);

        // 解析年月字符串
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月", Locale.CHINA);
        YearMonth yearMonth = YearMonth.parse(monthSearchDto.getSearchMonth(), formatter);
        // 获取当月的天数
        int daysInMonth = yearMonth.lengthOfMonth();
        // 创建每一天的对象
        List<DailyData> dailyDataList = new ArrayList<>(daysInMonth);
        for (int day = 1; day <= daysInMonth; day++) {
            LocalDate date = yearMonth.atDay(day);
            DailyData dailyData = new DailyData();
            dailyData.setDate(date);
            dailyDataList.add(dailyData);
        }

        for (DailyData dailyData : dailyDataList) {
            Integer totalAmount = 0;
            Integer finishedAmount = 0;
            for (AppointmentMaster appointmentMaster : appointmentMasterList) {
                if (!appointmentMaster.getStartTime().isBefore(dailyData.getDate().atStartOfDay()) &&  !appointmentMaster.getEndTime().isAfter(dailyData.getDate().atTime(23, 59, 59))) {
                    totalAmount+=1;
                    //判断预约是否已完成；
                    Boolean flag = booleanMap.get(appointmentMaster.getId());
                    if (flag) {
                        finishedAmount+=1;
                    }
                }

            }
            dailyData.setTotalAmount(totalAmount);
            dailyData.setFinishedAmount(finishedAmount);
        }

//        // 计算当天的开始时间（00:00:00）和结束时间（23:59:59）
//        LocalDateTime startTime = date.atStartOfDay();
//        LocalDateTime endTime = date.atTime(23, 59, 59);

        return Result.ok(dailyDataList);
    }

    public Map<Integer,Boolean> appointmentFinishedFlag(List<Integer> appointmentIds) {
        Map<Integer,Boolean> flagMap = new HashMap<>();

        for (Integer appointmentId : appointmentIds) {
            LambdaQueryWrapper<AppointmentProcess> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper
                    .eq(AppointmentProcess::getAppointmentId, appointmentId)
                    .orderByDesc(AppointmentProcess::getOperationTime) // 按操作时间降序
                    .last("LIMIT 1"); // 只取第一条（最新）

            AppointmentProcess latestProcess = appointmentProcessService.getOne(queryWrapper);
            Boolean flag = false;
            if (latestProcess.getAppointmentStatus().equals("已完成")){
                flag = true;
            }
            flagMap.put(appointmentId,flag);
        }
        return flagMap;
    }


    @PostMapping("/getDayAppointmentByDateSearch")
    public Result<List<HourData>> getDayAppointmentByDateSearch(@RequestBody DateSearchDto dateSearchDto){
        LambdaQueryWrapper<AppointmentMaster> queryWrapper = new LambdaQueryWrapper<>();

        Integer stationCount=0;
        if(dateSearchDto.getStoreId()!=null && dateSearchDto.getStoreId()>0){
            queryWrapper.eq(AppointmentMaster::getStoreId,dateSearchDto.getStoreId());
            Result<Integer> result = storeApi.getStationCountByStoreId(dateSearchDto.getStoreId());
            if(result!=null&&result.getCode()== ResultCode.SUCCESS.getCode()){
                Integer data1 = result.getData();
                stationCount=data1!=null?data1:0;
            }
        }

        log.info("stationCount:{}",stationCount);

        if (dateSearchDto.getSearchDate()!=null && !dateSearchDto.getSearchDate().equals("")) {
            queryWrapper.ge(AppointmentMaster::getStartTime,dateSearchDto.getSearchDate().atStartOfDay());
            queryWrapper.le(AppointmentMaster::getEndTime,dateSearchDto.getSearchDate().atTime(23, 59, 59));
        }
        List<AppointmentMaster> appointmentMasterList = appointmentMasterService.list(queryWrapper);


        List<String> timeList = new ArrayList<>();
        timeList.add(0,"8:00-9:00");
        timeList.add(1,"9:00-10:00");
        timeList.add(2,"10:00-11:00");
        timeList.add(3,"11:00-12:00");
        timeList.add(4,"14:00-15:00");
        timeList.add(5,"15:00-16:00");
        timeList.add(6,"16:00-17:00");
        timeList.add(7,"17:00-18:00");
        List<HourData> hourDataList = new ArrayList<>(timeList.size());

        for (int i = 0; i < 8; i++) {
            HourData hourData = new HourData();
            hourData.setTimeRange(timeList.get(i));
            TimeSlot timeSlot = generateTimeSlot(dateSearchDto.getSearchDate(),timeList.get(i));
            hourData.setStartTime(timeSlot.getStartTime());
            hourData.setEndTime(timeSlot.getEndTime());

            hourDataList.add(i,hourData);
        }

        List<Integer> userIds = new ArrayList<>();

        for (HourData hourData : hourDataList) {
            Integer remaining = stationCount;
            List<AppointmentMasterVo> masterVoList = new ArrayList<>();
            for (AppointmentMaster appointmentMaster : appointmentMasterList) {
                if(!hourData.getStartTime().isAfter(appointmentMaster.getStartTime()) &&  !hourData.getEndTime().isBefore(appointmentMaster.getEndTime()) ) {
                    remaining -= 1;
                    AppointmentMasterVo appointmentMasterVo = new AppointmentMasterVo();
                    appointmentMasterVo.setAppointmentId(appointmentMaster.getId());
                    appointmentMasterVo.setStoreId(appointmentMaster.getStoreId());
                    appointmentMasterVo.setUserId(appointmentMaster.getUserId());

                    masterVoList.add(appointmentMasterVo);
                    userIds.add(appointmentMaster.getUserId());
                }
            }
            hourData.setRemaining(remaining);
            hourData.setMasterVos(masterVoList);
        }

        List<UserDto> userDtoList = new ArrayList<>();
        Result<List<UserDto>> result = staffApi.getUserDtosByUserIds(userIds);
        if(result!=null&&result.getCode()== ResultCode.SUCCESS.getCode()){
            List<UserDto> data = result.getData();
            userDtoList=data!=null?data:new ArrayList<>();
        }

        HashMap<Integer,UserDto> userDtoHashMap = new HashMap<>();
        for (UserDto userDto : userDtoList) {
            userDtoHashMap.put(userDto.getId(), userDto);
        }
        for (HourData hourData : hourDataList) {
            List<AppointmentMasterVo> masterVos = hourData.getMasterVos();
            for (AppointmentMasterVo masterVo : masterVos) {
                UserDto userDto = userDtoHashMap.get(masterVo.getUserId());
                masterVo.setUserName(userDto != null ? userDto.getUserName() : "未知用户");
            }
        }

        return Result.ok(hourDataList);

    }

    public TimeSlot generateTimeSlot(LocalDate baseDate, String timeRange) {

        TimeSlot timeSlot = new TimeSlot();

        // 分割时段字符串（例如："8:00-9:00" → ["8:00", "9:00"]）
        String[] parts = timeRange.split("-");
        String startTimeStr = parts[0]; // "8:00"
        String endTimeStr = parts[1];   // "9:00"

        // 提取小时和分钟
        String[] startParts = startTimeStr.split(":");
        int startHour = Integer.parseInt(startParts[0]);
        int startMinute = Integer.parseInt(startParts[1]);

        String[] endParts = endTimeStr.split(":");
        int endHour = Integer.parseInt(endParts[0]);
        int endMinute = Integer.parseInt(endParts[1]);

        // 生成完整的 LocalDateTime 对象
        LocalDateTime startTime = baseDate.atTime(startHour, startMinute);
        LocalDateTime endTime = baseDate.atTime(endHour, endMinute);

        // 关键：设置时间字段到TimeSlot对象
        timeSlot.setStartTime(startTime);
        timeSlot.setEndTime(endTime);
        return timeSlot;
    }


}
